{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Executing app locally\n",
    "\n",
    "## Executing and Inspecting App in Jupyter Notebook\n",
    "\n",
    "You can define Operators and Application in Jupyter Notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from monai.deploy.conditions import CountCondition\n",
    "from monai.deploy.core import (Application, ConditionType, ExecutionContext, InputContext,\n",
    "                               Operator, OperatorSpec, OutputContext)\n",
    "\n",
    "class TaskA(Operator):\n",
    "    def setup(self, spec: OperatorSpec):\n",
    "        spec.input(\"input_path\").condition(ConditionType.NONE)  # optional input\n",
    "        spec.output(\"A\")\n",
    "\n",
    "    def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):\n",
    "        input_path = op_input.receive(\"input_path\")  # Get input path via named input. Not used here.\n",
    "        if not input_path:\n",
    "            print(\"TaskA receives None at optional input.\")\n",
    "        data = 1\n",
    "        op_output.emit(data, \"A\")\n",
    "        print(f\"TaskA emits {data}\")\n",
    "\n",
    "class TaskB(Operator):\n",
    "    def setup(self, spec: OperatorSpec):\n",
    "        spec.input(\"input\")\n",
    "        spec.output(\"B\").condition(ConditionType.NONE)  # optional output, not requiring receiver\n",
    "\n",
    "    def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):\n",
    "        input_data = op_input.receive(\"input\")\n",
    "        print(f\"TaskB receives {input_data}\")\n",
    "\n",
    "        output_data = input_data + 1\n",
    "        op_output.emit(output_data, \"B\")\n",
    "        print(f\"TaskB emits {output_data}\")\n",
    "\n",
    "class App(Application):\n",
    "    def compose(self):\n",
    "        taskA = TaskA(self, CountCondition(self, 1), name=\"Task A\")  # self and name are required\n",
    "        taskB = TaskB(self, name=\"Task B\")\n",
    "        self.add_flow(taskA, taskB) # Need not explicitly connect single output and input\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    App().run()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once an Application class (`App`) is defined, you can instantiate the application and execute with <a href=\"../modules/_autosummary/monai.deploy.core.Application.html#monai.deploy.core.Application.run\">Application.run()</a> method.\n",
    "\n",
    "Since the above example doesn't use input or output paths, we need not them, otherwise, environment variables can be used to provide the paths, as in this [example](../getting_started/tutorials/simple_app)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "app = App()\n",
    "app.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "app.argv"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can access [Graph](/modules/_autosummary/monai.deploy.core.graphs.Graph) object through `Application.graph`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(app.graph)\n",
    "graph = app.graph\n",
    "print(f\"graph.context: {graph.context}\")\n",
    "operators = graph.get_nodes()\n",
    "print(f\"get_nodes.get_nodes(): {operators}\")\n",
    "print(f\"graph.is_root(operators[0]): {graph.is_root(operators[0])}\")\n",
    "print(f\"graph.is_leaf(operators[1]): {graph.is_leaf(operators[1])}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Executing Application\n",
    "\n",
    "Once the application is verified inside Jupyter notebook, we can write the whole application as a file(`app.py`) with the following lines at the end of the file:\n",
    "\n",
    "```python\n",
    "if __name__ == \"__main__\":\n",
    "    App(do_run=True)\n",
    "```\n",
    "\n",
    "Above lines are needed to execute the application code by using `python` interpreter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile app.py\n",
    "from monai.deploy.conditions import CountCondition\n",
    "from monai.deploy.core import (Application, ConditionType, ExecutionContext, InputContext,\n",
    "                               Operator, OperatorSpec, OutputContext)\n",
    "\n",
    "class TaskA(Operator):\n",
    "    def setup(self, spec: OperatorSpec):\n",
    "        spec.input(\"input_path\").condition(ConditionType.NONE)  # optional input\n",
    "        spec.output(\"A\")\n",
    "\n",
    "    def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):\n",
    "        input_path = op_input.receive(\"input_path\")  # Get input path via named input. Not used here.\n",
    "        if not input_path:\n",
    "            print(\"TaskA receives None at optional input.\")\n",
    "        data = 1\n",
    "        op_output.emit(data, \"A\")\n",
    "        print(f\"TaskA emits {data}\")\n",
    "\n",
    "class TaskB(Operator):\n",
    "    def setup(self, spec: OperatorSpec):\n",
    "        spec.input(\"input\")\n",
    "        spec.output(\"B\").condition(ConditionType.NONE)  # optional output, not requiring receiver\n",
    "\n",
    "    def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):\n",
    "        input_data = op_input.receive(\"input\")\n",
    "        print(f\"TaskB receives {input_data}\")\n",
    "\n",
    "        output_data = input_data + 1\n",
    "        op_output.emit(output_data, \"B\")\n",
    "        print(f\"TaskB emits {output_data}\")\n",
    "\n",
    "class App(Application):\n",
    "    def compose(self):\n",
    "        taskA = TaskA(self, CountCondition(self, 1), name=\"Task A\")  # self and name are required\n",
    "        taskB = TaskB(self, name=\"Task B\")\n",
    "        self.add_flow(taskA, taskB) # Need not explicitly connect single output and input\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    App().run()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's run the application"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!python app.py"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
